home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 1999 #2 / Amiga Plus CD - 1999 - No. 2.iso / System-Boost / Workbench / ToolManager / Source / Prefs / config.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  25KB  |  886 lines

  1. /*
  2.  * config.c  V3.1
  3.  *
  4.  * Preferences editor configuration file handling routines
  5.  *
  6.  * Copyright (C) 1990-98 Stefan Becker
  7.  *
  8.  * This source code is for educational purposes only. You may study it
  9.  * and copy ideas or algorithms from it for your own projects. It is
  10.  * not allowed to use any of the source codes (in full or in parts)
  11.  * in other programs. Especially it is not allowed to create variants
  12.  * of ToolManager or ToolManager-like programs from this source code.
  13.  *
  14.  */
  15.  
  16. #include "toolmanager.h"
  17.  
  18. /* Local data */
  19. static const char  TMConfigVersion[]  = TMCONFIGVERSION;
  20. static const char *DefaultToolTypes[] = {"USE", NULL };
  21.  
  22. /* Local macros */
  23. #define TREENODE(n) ((struct MUIS_Listtree_TreeNode *) (n))
  24.  
  25. /* IntuiMsg handler */
  26. __geta4 static void IntuiMsgFunction(__a1 struct IntuiMessage *msg,
  27.                                      __a2 struct FileRequester *req)
  28. {
  29.  /* Refresh window? */
  30.  if (msg->Class == IDCMP_REFRESHWINDOW)
  31.  
  32.   /* Send message to application */
  33.   DoMethod(req->fr_UserData, MUIM_Application_CheckRefresh);
  34. }
  35.  
  36. static const struct Hook IntuiMsgHook = {
  37.  {NULL, NULL}, (void *) IntuiMsgFunction, NULL, NULL
  38. };
  39.  
  40. /* Handle ASL file requester */
  41. #undef  DEBUGFUNCTION
  42. #define DEBUGFUNCTION OpenFileRequester
  43. static const char *OpenFileRequester(Object *wobj, const char *name,
  44.                                      const char *title, BOOL save)
  45. {
  46.  char *pathpart;
  47.  char *rc       = NULL;
  48.  
  49.  /* Allocate memory for old file name */
  50.  if (pathpart = DuplicateString(name)) {
  51.   Object               *app      = _app(wobj);
  52.   struct FileRequester *fr;
  53.   char                 *filepart;
  54.   struct Window        *w;
  55.  
  56.   CONFIG_LOG(LOG1(Path buffer, "0x%08lx", pathpart))
  57.  
  58.   /* Get file part */
  59.   filepart = FilePart(name);
  60.  
  61.   /* Append string terminator after path part */
  62.   pathpart[filepart - name] = '\0';
  63.  
  64.   CONFIG_LOG(LOG3(Split, "Name %s Path %s File %s", name, pathpart, filepart))
  65.  
  66.   /* Get window pointer */
  67.   GetAttr(MUIA_Window_Window, wobj, (ULONG *) &w);
  68.  
  69.   /* Allocate file requester */
  70.   if (fr = MUI_AllocAslRequestTags(ASL_FileRequest,
  71.        ASLFR_Window,          w,
  72.        ASLFR_TitleText,       title,
  73.        ASLFR_InitialLeftEdge, w->LeftEdge + w->BorderLeft + 2,
  74.        ASLFR_InitialTopEdge,  w->TopEdge  + w->BorderTop  + 2,
  75.        ASLFR_InitialWidth,    w->Width    - w->BorderLeft - w->BorderRight  - 4,
  76.        ASLFR_InitialHeight,   w->Height   - w->BorderTop  - w->BorderBottom - 4,
  77.        ASLFR_InitialPattern,  "#?.prefs",
  78.        ASLFR_InitialDrawer,   pathpart,
  79.        ASLFR_InitialFile,     filepart,
  80.        ASLFR_DoPatterns,      TRUE,
  81.        ASLFR_DoSaveMode,      save,
  82.        ASLFR_RejectIcons,     TRUE,
  83.        ASLFR_UserData,        app,
  84.        ASLFR_IntuiMsgFunc,    &IntuiMsgHook,
  85.        TAG_DONE)) {
  86.  
  87.    CONFIG_LOG(LOG1(Requester, "0x%08lx", fr))
  88.  
  89.    /* Put MUI application to sleep */
  90.    SetAttrs(app, MUIA_Application_Sleep, TRUE, TAG_DONE);
  91.  
  92.    /* Open requester */
  93.    if (MUI_AslRequestTags(fr, TAG_DONE)) {
  94.  
  95.     CONFIG_LOG(LOG0(Requester done))
  96.  
  97.     /* File name valid? */
  98.     if (*fr->fr_File) {
  99.      ULONG len = strlen(fr->fr_Drawer) + strlen(fr->fr_File) + 2;
  100.  
  101.      CONFIG_LOG(LOG4(Selected, "Path %s (0x%08lx) File %s (0x%08lx)",
  102.                      fr->fr_Drawer, fr->fr_Drawer, fr->fr_File, fr->fr_File))
  103.  
  104.      /* Allocate memory for new file name */
  105.      if (rc = GetVector(len)) {
  106.  
  107.       /* Create new file name */
  108.       strcpy(rc, fr->fr_Drawer);
  109.       AddPart(rc, fr->fr_File, len);
  110.  
  111.       CONFIG_LOG(LOG1(New file, "%s", rc))
  112.      }
  113.     }
  114.    }
  115.  
  116.    /* Wake up MUI application */
  117.    SetAttrs(app, MUIA_Application_Sleep, FALSE, TAG_DONE);
  118.  
  119.    /* Free ASL requester */
  120.    MUI_FreeAslRequest(fr);
  121.   }
  122.  
  123.   /* Release old file name */
  124.   FreeVector(pathpart);
  125.  }
  126.  
  127.  CONFIG_LOG(LOG1(Result, "0x%08lx", rc))
  128.  
  129.  /* Return pointer to new file name (caller has to FreeVector() it! */
  130.  return(rc);
  131. }
  132.  
  133. /* Set quiet mode of all lists */
  134. #undef  DEBUGFUNCTION
  135. #define DEBUGFUNCTION SetQuietMode
  136. static void SetQuietMode(Object **lists, ULONG value)
  137. {
  138.  int i;
  139.  
  140.  CONFIG_LOG(LOG0(Entry))
  141.  
  142.  /* For all object types */
  143.  for (i = TMOBJTYPE_EXEC; i < TMOBJTYPES; i++, lists++)
  144.  
  145.   /* Set list quiet mode */
  146.   SetAttrs(*lists, MUIA_Listtree_Quiet, value, TAG_DONE);
  147. }
  148.  
  149. /* Create object from FORM */
  150. #undef  DEBUGFUNCTION
  151. #define DEBUGFUNCTION CreateObjectFromFORM
  152. static BOOL CreateObjectFromFORM(struct IFFHandle *iffh, Object **lists,
  153.                                  ULONG type,
  154.                                  struct MUIS_Listtree_TreeNode *node)
  155. {
  156.  Object *obj;
  157.  BOOL    rc  = FALSE;
  158.  
  159.  /* Create object */
  160.  if (obj = NewObject(ObjectClasses[type]->mcc_Class, NULL,
  161.                                                      TMA_Name, TextGlobalEmpty,
  162.                                                      TMA_List, lists[type],
  163.                                                      TAG_DONE)) {
  164.  
  165.   CONFIG_LOG(LOG1(Object, "0x%08lx", obj))
  166.  
  167.   /* Tell object to parse the IFF chunk */
  168.   if (DoMethod(obj, TMM_ParseIFF, iffh, lists)) {
  169.  
  170.    CONFIG_LOG(LOG0(IFF chunk parse OK))
  171.  
  172.    /* Append object to list */
  173.    if (DoMethod(lists[type], MUIM_Listtree_Insert, TextGlobalEmpty, obj, node,
  174.                 MUIV_Listtree_Insert_PrevNode_Tail, 0)) {
  175.  
  176.     CONFIG_LOG(LOG0(Object inserted))
  177.  
  178.     /* All OK */
  179.     rc = TRUE;
  180.    }
  181.   }
  182.  
  183.   /* Error? Delete object again */
  184.   if (rc == FALSE) MUI_DisposeObject(obj);
  185.  }
  186.  
  187.  return(rc);
  188. }
  189.  
  190. /* Open group */
  191. #undef  DEBUGFUNCTION
  192. #define DEBUGFUNCTION OpenGroup
  193. static struct MUIS_Listtree_TreeNode *OpenGroup(struct IFFHandle *iffh,
  194.                                                 Object *list, ULONG type)
  195. {
  196.  struct MUIS_Listtree_TreeNode *rc = NULL;
  197.  
  198.  /* Parse PROP chunk */
  199.  if ((PropChunk(iffh, type, ID_OGRP) == 0) &&
  200.      (StopOnExit(iffh, type, ID_PROP) == 0) &&
  201.      (ParseIFF(iffh, IFFPARSE_SCAN) == IFFERR_EOC)) {
  202.   struct StoredProperty *sp;
  203.  
  204.   CONFIG_LOG(LOG0(PROP chunk parsed))
  205.  
  206.   /* Get group chunk */
  207.   if (sp = FindProp(iffh, type, ID_OGRP)) {
  208.    struct MUIS_Listtree_TreeNode *next =
  209.                                 TREENODE(MUIV_Listtree_GetEntry_Position_Head);
  210.  
  211.    CONFIG_LOG(LOG2(Group property, "%s (0x%08lx)", sp->sp_Data, sp->sp_Data))
  212.  
  213.    /* Start from root node */
  214.    rc = TREENODE(MUIV_Listtree_GetEntry_ListNode_Root);
  215.  
  216.    /* Scan current list */
  217.    while (rc = TREENODE(DoMethod(list, MUIM_Listtree_GetEntry, rc, next, 0))) {
  218.     char *name;
  219.  
  220.     CONFIG_LOG(LOG1(Next node, "%s", rc))
  221.  
  222.     /* Get group name */
  223.     GetAttr(TMA_Name, (Object *) rc->tn_User, (ULONG *) &name);
  224.  
  225.     /* Group found? Yes, leave loop */
  226.     if (strcmp(sp->sp_Data, name) == 0) break;
  227.  
  228.     /* Search for next object on the same level */
  229.     next = TREENODE(MUIV_Listtree_GetEntry_Position_Next);
  230.    }
  231.  
  232.    /* Group not found? */
  233.    if (rc == NULL) {
  234.     Object *group;
  235.  
  236.     /* Group not found, create new one */
  237.     if (group = NewObject(GroupClass->mcc_Class, NULL, TMA_Name, sp->sp_Data,
  238.                                                        TMA_List, list,
  239.                                                        TAG_DONE)) {
  240.  
  241.      CONFIG_LOG(LOG1(New Group, "0x%08lx", group))
  242.  
  243.      /* Append group to list */
  244.      if ((rc = TREENODE(DoMethod(list, MUIM_Listtree_Insert, sp->sp_Data,
  245.                                  group, MUIV_Listtree_Insert_ListNode_Root,
  246.                                  MUIV_Listtree_Insert_PrevNode_Tail,
  247.                                  TNF_LIST))) == NULL)
  248.  
  249.       /* Can't append group, delete group object again */
  250.       MUI_DisposeObject(group);
  251.     }
  252.    }
  253.   }
  254.  }
  255.  
  256.  CONFIG_LOG(LOG1(Result, "0x%08lx", rc))
  257.  
  258.  /* Return pointer to group */
  259.  return(rc);
  260. }
  261.  
  262. /* Parse configuration */
  263. #undef  DEBUGFUNCTION
  264. #define DEBUGFUNCTION ParseConfig
  265. static BOOL ParseConfig(struct IFFHandle *iffh, Object **lists)
  266. {
  267.  ULONG                          CurrentType  = ID_TMGP;
  268.  ULONG                          CurrentClass;
  269.  struct MUIS_Listtree_TreeNode *CurrentGroup;
  270.  BOOL                           rc           = TRUE;
  271.  BOOL                           notend       = TRUE;
  272.  
  273.  /* Parse as long as no error occurs */
  274.  while (rc && notend) {
  275.  
  276.   /* Next parse step */
  277.   switch (ParseIFF(iffh, IFFPARSE_STEP)) {
  278.  
  279.    case 0: {
  280.      struct ContextNode *cn;
  281.  
  282.      /* Normal parse step, get current chunk */
  283.      if (cn = CurrentChunk(iffh))
  284.  
  285.       /* Which chunk type? */
  286.       switch (cn->cn_ID) {
  287.        case ID_LIST:
  288.         CONFIG_LOG(LOG2(Enter LIST, "Type 0x%08lx Size %ld",
  289.                         cn->cn_Type, cn->cn_Size))
  290.  
  291.         /* Store & check type and get corresponding class & list */
  292.         switch (CurrentType = cn->cn_Type) {
  293.          case ID_TMEX: CurrentClass = TMOBJTYPE_EXEC;   break;
  294.          case ID_TMIM: CurrentClass = TMOBJTYPE_IMAGE;  break;
  295.          case ID_TMSO: CurrentClass = TMOBJTYPE_SOUND;  break;
  296.          case ID_TMMO: CurrentClass = TMOBJTYPE_MENU;   break;
  297.          case ID_TMIC: CurrentClass = TMOBJTYPE_ICON;   break;
  298.          case ID_TMDO: CurrentClass = TMOBJTYPE_DOCK;   break;
  299.          case ID_TMAC: CurrentClass = TMOBJTYPE_ACCESS; break;
  300.  
  301.          default:
  302.           CONFIG_LOG(LOG0(Unknown LIST))
  303.           rc = FALSE;
  304.           break;
  305.         }
  306.         break;
  307.  
  308.        case ID_PROP:
  309.         CONFIG_LOG(LOG2(Enter PROP, "Type 0x%08lx Size %ld",
  310.                         cn->cn_Type, cn->cn_Size))
  311.  
  312.         /* Open group */
  313.         if ((CurrentGroup = OpenGroup(iffh, lists[CurrentClass], CurrentType))
  314.              == NULL) {
  315.          CONFIG_LOG(LOG0(Could not open group))
  316.          rc = FALSE;
  317.         }
  318.         break;
  319.  
  320.        case ID_FORM:
  321.         CONFIG_LOG(LOG2(Enter FORM, "Type 0x%08lx Size %ld",
  322.                         cn->cn_Type, cn->cn_Size))
  323.  
  324.         /* Is the type correct? */
  325.         if (cn->cn_Type == CurrentType)
  326.  
  327.          /* Yes, global parameters or object chunk? */
  328.          rc = (cn->cn_Type == ID_TMGP) ? ParseGlobalIFF(iffh) :
  329.                                          CreateObjectFromFORM(iffh,
  330.                                                               lists,
  331.                                                               CurrentClass,
  332.                                                               CurrentGroup);
  333.         else {
  334.          /* Unexpected FORM type */
  335.          CONFIG_LOG(LOG0(Unexpected FORM!))
  336.          rc = FALSE;
  337.         }
  338.         break;
  339.  
  340.        default:
  341.         /* No LIST/PROP/FORM */
  342.         CONFIG_LOG(LOG0(No LIST/PROP/FORM!))
  343.         rc = FALSE;
  344.         break;
  345.       }
  346.  
  347.      else {
  348.       CONFIG_LOG(LOG0(No current chunk?!?))
  349.       rc = FALSE;
  350.      }
  351.     }
  352.     break;
  353.  
  354.    case IFFERR_EOC:
  355. #ifdef DEBUG
  356.     {
  357.      struct ContextNode *cn;
  358.  
  359.      /* End of chunk reached. Get current chunk */
  360.      if (cn = CurrentChunk(iffh))
  361.  
  362.       /* Which type? */
  363.       switch(cn->cn_ID) {
  364.        case ID_LIST:
  365.         CONFIG_LOG(LOG2(Leave LIST, "Type 0x%08lx Size %ld",
  366.                         cn->cn_Type, cn->cn_Size))
  367.         break;
  368.  
  369.        case ID_FORM:
  370.         CONFIG_LOG(LOG2(Leave FORM, "Type 0x%08lx Size %ld",
  371.                         cn->cn_Type, cn->cn_Size))
  372.         break;
  373.  
  374.        default:
  375.         CONFIG_LOG(LOG3(Leave unknown context,
  376.                         "ID 0x%08lx Type 0x%08lx Size %ld",
  377.                         cn->cn_ID, cn->cn_Type, cn->cn_Size))
  378.         break;
  379.       }
  380.     }
  381. #endif
  382.     break;
  383.  
  384.    case IFFERR_EOF:
  385.     /* End of configuration reached. All OK! */
  386.     CONFIG_LOG(LOG0(Configuration parsed OK!))
  387.     notend = FALSE;
  388.     break;
  389.  
  390.    default:
  391.     CONFIG_LOG(LOG0(Error in parsing))
  392.     rc = FALSE;
  393.     break;
  394.   }
  395.  }
  396.  
  397.  CONFIG_LOG(LOG1(Result, "%ld", rc))
  398.  
  399.  return(rc);
  400. }
  401.  
  402. /* Read configuration file */
  403. #undef  DEBUGFUNCTION
  404. #define DEBUGFUNCTION ReadConfig
  405. void ReadConfig(Object *wobj, Object **lists, BOOL delete, const char *name)
  406. {
  407.  struct IFFHandle *iffh;
  408.  BOOL              rc   = FALSE;
  409.  
  410.  CONFIG_LOG(LOG2(Name, "%s (0x%08lx)", name, name))
  411.  
  412.  /* Put application to sleep */
  413.  SetAttrs(_app(wobj), MUIA_Application_Sleep, TRUE, TAG_DONE);
  414.  
  415.  /* Allocate IFF handle */
  416.  if (iffh = AllocIFF()) {
  417.  
  418.   CONFIG_LOG(LOG1(IFFHandle, "0x%08lx", iffh))
  419.  
  420.   /* Open configuration file */
  421.   if (iffh->iff_Stream = Open(name, MODE_OLDFILE)) {
  422.  
  423.    CONFIG_LOG(LOG1(File, "0x%08lx", iffh->iff_Stream))
  424.  
  425.    /* Initialize IFF handle */
  426.    InitIFFasDOS(iffh);
  427.  
  428.    /* Open IFF handle */
  429.    if (OpenIFF(iffh, IFFF_READ) == 0) {
  430.  
  431.     CONFIG_LOG(LOG0(Handle open))
  432.  
  433.     /* Start IFF parsing */
  434.     if (ParseIFF(iffh, IFFPARSE_STEP) == 0) {
  435.      struct ContextNode *cn;
  436.  
  437.      CONFIG_LOG(LOG0(First parse step))
  438.  
  439.      /* a) Check IFF type: FORM TMPR */
  440.      /* b) Step to version chunk     */
  441.      if ((cn = CurrentChunk(iffh)) &&
  442.          (cn->cn_ID == ID_FORM) && (cn->cn_Type == ID_TMPR) &&
  443.          (ParseIFF(iffh, IFFPARSE_STEP) == 0) &&
  444.          (cn = CurrentChunk(iffh)) &&
  445.          (cn->cn_ID == ID_FVER) && (cn->cn_Size == sizeof(TMCONFIGVERSION))) {
  446.       char *buf;
  447.  
  448.       CONFIG_LOG(LOG0(Version chunk found))
  449.  
  450.       /* Allocate memory for version chunk */
  451.       if (buf = GetMemory(sizeof(TMCONFIGVERSION))) {
  452.  
  453.        /* Read version chunk and check version */
  454.        if ((ReadChunkBytes(iffh, buf, sizeof(TMCONFIGVERSION))
  455.              == sizeof(TMCONFIGVERSION)) &&
  456.            (strcmp(buf, TMConfigVersion) == 0) &&
  457.            (ParseIFF(iffh, IFFPARSE_STEP) == IFFERR_EOC)) {
  458.  
  459.         CONFIG_LOG(LOG0(Configuration file OK))
  460.  
  461.         /* Set lists to quiet mode */
  462.         SetQuietMode(lists, TRUE);
  463.  
  464.         /* Delete old configuration? */
  465.         if (delete) {
  466.          int i;
  467.  
  468.          /* For all object types */
  469.          for (i = TMOBJTYPE_ACCESS; i >= TMOBJTYPE_EXEC; i--)
  470.  
  471.           /* Delete all entries in list */
  472.           DoMethod(lists[i], MUIM_Listtree_Remove,
  473.                    MUIV_Listtree_Remove_ListNode_Root,
  474.                    MUIV_Listtree_Remove_TreeNode_All, 0);
  475.  
  476.         }
  477.  
  478.         /* Parse configuration */
  479.         rc = ParseConfig(iffh, lists);
  480.  
  481.         /* Set lists to verbose mode */
  482.         SetQuietMode(lists, FALSE);
  483.        }
  484.  
  485.        /* Free version chunk */
  486.        FreeMemory(buf, sizeof(TMCONFIGVERSION));
  487.       }
  488.      }
  489.     }
  490.  
  491.     /* Close IFF handle */
  492.     CloseIFF(iffh);
  493.    }
  494.  
  495.    /* Close file */
  496.    Close(iffh->iff_Stream);
  497.   }
  498.  
  499.   /* Free IFF handle */
  500.   FreeIFF(iffh);
  501.  }
  502.  
  503.  /* Wake application up */
  504.  SetAttrs(_app(wobj), MUIA_Application_Sleep, FALSE, TAG_DONE);
  505.  
  506.  /* Error? */
  507.  if (rc == FALSE) {
  508.  
  509.   CONFIG_LOG(LOG0(Error in config file read))
  510.  
  511.   MUI_Request(_app(wobj), wobj, 0,
  512.               TextGlobalTitle, TextGlobalCancel,
  513.               TranslateString(LOCALE_TEXT_CONFIG_READ_ERROR_STR,
  514.                               LOCALE_TEXT_CONFIG_READ_ERROR),
  515.               name);
  516.  }
  517. }
  518.  
  519. /* Read configuration file using file requester */
  520. #undef  DEBUGFUNCTION
  521. #define DEBUGFUNCTION ReadConfigWithRequester
  522. const char *ReadConfigWithRequester(Object *wobj, Object **lists, BOOL delete,
  523.                                     const char *name)
  524. {
  525.  const char *title = delete ?
  526.                            TranslateString(LOCALE_TEXT_CONFIG_LOAD_FILE_STR,
  527.                                            LOCALE_TEXT_CONFIG_LOAD_FILE) :
  528.                            TranslateString(LOCALE_TEXT_CONFIG_APPEND_FILE_STR,
  529.                                            LOCALE_TEXT_CONFIG_APPEND_FILE);
  530.  const char *newname;
  531.  
  532.  /* Open file requester */
  533.  if (newname = OpenFileRequester(wobj, name, title, FALSE)) {
  534.  
  535.   CONFIG_LOG(LOG2(File, "%s (0x%08lx)", newname, newname))
  536.  
  537.   /* Read configuration from file */
  538.   ReadConfig(wobj, lists, delete, newname);
  539.  }
  540.  
  541.  CONFIG_LOG(LOG1(Result, "0x%08lx", newname))
  542.  
  543.  /* Return pointer to new file name. Caller has to FreeVector() it! */
  544.  return(newname);
  545. }
  546.  
  547. /* Write configuration file */
  548. #undef  DEBUGFUNCTION
  549. #define DEBUGFUNCTION WriteConfig
  550. BOOL WriteConfig(Object *wobj, Object **lists, const char *name, BOOL icons)
  551. {
  552.  struct IFFHandle *iffh;
  553.  BOOL              rc   = FALSE;
  554.  
  555.  CONFIG_LOG(LOG2(Name, "%s (0x%08lx)", name, name))
  556.  
  557.  /* Put application to sleep */
  558.  SetAttrs(_app(wobj), MUIA_Application_Sleep, TRUE, TAG_DONE);
  559.  
  560.  /* Allocate IFF handle */
  561.  if (iffh = AllocIFF()) {
  562.  
  563.   CONFIG_LOG(LOG1(IFFHandle, "0x%08lx", iffh))
  564.  
  565.   /* Open configuration file */
  566.   if (iffh->iff_Stream = Open(name, MODE_NEWFILE)) {
  567.    ULONG protection = 0;
  568.  
  569.    CONFIG_LOG(LOG1(File, "0x%08lx", iffh->iff_Stream))
  570.  
  571.    /* Set protection bits */
  572.    {
  573.     struct FileInfoBlock *fib;
  574.  
  575.     /* Allocate file info block */
  576.     if (fib = AllocDosObject(DOS_FIB, NULL)) {
  577.  
  578.      CONFIG_LOG(LOG1(FIB, "0x%08lx", fib))
  579.  
  580.      /* Examine file */
  581.      if (ExamineFH(iffh->iff_Stream, fib)) {
  582.  
  583.       CONFIG_LOG(LOG1(Old protection bits, "0x%08lx", fib->fib_Protection))
  584.  
  585.       /* Copy protection bits */
  586.       protection = fib->fib_Protection;
  587.      }
  588.  
  589.      /* Free file info block */
  590.      FreeDosObject(DOS_FIB, fib);
  591.     }
  592.    }
  593.  
  594.    /* Initialize IFF handle */
  595.    InitIFFasDOS(iffh);
  596.  
  597.    /* Open IFF handle */
  598.    if (OpenIFF(iffh, IFFF_WRITE) == 0) {
  599.  
  600.     CONFIG_LOG(LOG0(Handle open))
  601.  
  602.     /* Prepare new configuration file  */
  603.     /* a) Push FORM TMPR chunk         */
  604.     /* b) Push, write & pop FVER chunk */
  605.     /* c) Write global data            */
  606.     if ((PushChunk(iffh, ID_TMPR, ID_FORM, IFFSIZE_UNKNOWN) == 0) &&
  607.         (PushChunk(iffh, 0,       ID_FVER, IFFSIZE_UNKNOWN) == 0) &&
  608.         (WriteChunkBytes(iffh, TMConfigVersion, sizeof(TMCONFIGVERSION))
  609.          == sizeof(TMCONFIGVERSION)) &&
  610.         (PopChunk(iffh) == 0) &&
  611.         WriteGlobalIFF(iffh)) {
  612.      int i;
  613.  
  614.      CONFIG_LOG(LOG0(Configuration file prepared))
  615.  
  616.      /* Reset error flag */
  617.      rc = TRUE;
  618.  
  619.      /* For each object type */
  620.      for (i = TMOBJTYPE_EXEC; rc && (i < TMOBJTYPES); i++) {
  621.       Object                        *CurrentList  = *lists++;
  622.       struct MUIS_Listtree_TreeNode *CurrentGroup;
  623.  
  624.       CONFIG_LOG(LOG1(Next list, "0x%08lx", CurrentList))
  625.  
  626.       /* Get first group in list */
  627.       if (CurrentGroup = TREENODE(DoMethod(CurrentList, MUIM_Listtree_GetEntry,
  628.                                     MUIV_Listtree_GetEntry_ListNode_Root,
  629.                                     MUIV_Listtree_GetEntry_Position_Head,
  630.                                     0))) {
  631.        ULONG   CurrentType;
  632.  
  633.        CONFIG_LOG(LOG1(First group, "0x%08lx", CurrentGroup))
  634.  
  635.        /* Get chunk ID from object type */
  636.        switch (i) {
  637.         case TMOBJTYPE_EXEC:   CurrentType = ID_TMEX; break;
  638.         case TMOBJTYPE_IMAGE:  CurrentType = ID_TMIM; break;
  639.         case TMOBJTYPE_SOUND:  CurrentType = ID_TMSO; break;
  640.         case TMOBJTYPE_MENU:   CurrentType = ID_TMMO; break;
  641.         case TMOBJTYPE_ICON:   CurrentType = ID_TMIC; break;
  642.         case TMOBJTYPE_DOCK:   CurrentType = ID_TMDO; break;
  643.         case TMOBJTYPE_ACCESS: CurrentType = ID_TMAC; break;
  644.        }
  645.  
  646.        /* Open LIST */
  647.        if (PushChunk(iffh, CurrentType, ID_LIST, IFFSIZE_UNKNOWN) == 0) {
  648.  
  649.         CONFIG_LOG(LOG1(LIST opened, "Type 0x%08lx", CurrentType))
  650.  
  651.  
  652.         /* For each group */
  653.         do {
  654.          ULONG  grouplen;
  655.          char  *groupname;
  656.  
  657.          CONFIG_LOG(LOG1(Next group, "0x%08lx", CurrentGroup))
  658.  
  659.          /* Get group name */
  660.          GetAttr(TMA_Name, CurrentGroup->tn_User, (ULONG *) &groupname);
  661.          grouplen = strlen(groupname) + 1;
  662.  
  663.          CONFIG_LOG(LOG2(Group name, "%s (0x%08lx)", groupname, groupname))
  664.  
  665.          /* Write group property */
  666.          if ((PushChunk(iffh, CurrentType, ID_PROP, IFFSIZE_UNKNOWN) == 0) &&
  667.              (PushChunk(iffh, 0,           ID_OGRP, IFFSIZE_UNKNOWN) == 0) &&
  668.              (WriteChunkBytes(iffh, groupname, grouplen) == grouplen) &&
  669.              (PopChunk(iffh) == 0) && /* OGRP */
  670.              (PopChunk(iffh) == 0)) { /* PROP */
  671.           struct MUIS_Listtree_TreeNode *tn  = CurrentGroup;
  672.           struct MUIS_Listtree_TreeNode *pos =
  673.                                 TREENODE(MUIV_Listtree_GetEntry_Position_Head);
  674.  
  675.           CONFIG_LOG(LOG0(Group PROP written))
  676.  
  677.           /* For each object in node */
  678.           while (rc && (tn = TREENODE(DoMethod(CurrentList,
  679.                                    MUIM_Listtree_GetEntry, tn, pos, 0)))) {
  680.  
  681.            CONFIG_LOG(LOG1(Next object, "0x%08lx", tn->tn_User))
  682.  
  683.            /* a) Open new FORM chunk                        */
  684.            /* b) Tell object to write its data to the chunk */
  685.            /* c) Close FORM chunk */
  686.            rc = (PushChunk(iffh, CurrentType, ID_FORM, IFFSIZE_UNKNOWN)
  687.                  == 0) &&
  688.                 DoMethod(tn->tn_User, TMM_WriteIFF, iffh) &&
  689.                 (PopChunk(iffh) == 0);
  690.  
  691.            /* Search for next object on the same level */
  692.            pos = TREENODE(MUIV_Listtree_GetEntry_Position_Next);
  693.           }
  694.  
  695.          } else {
  696.           CONFIG_LOG(LOG0(Could not write group PROP))
  697.           rc = FALSE;
  698.          }
  699.  
  700.         /* Get next group */
  701.         } while (rc &&
  702.                  (CurrentGroup = TREENODE(DoMethod(CurrentList,
  703.                                     MUIM_Listtree_GetEntry,
  704.                                     CurrentGroup,
  705.                                     MUIV_Listtree_GetEntry_Position_Next,
  706.                                     0))));
  707.  
  708.         /* Close LIST */
  709.         if (PopChunk(iffh) != 0) {
  710.          CONFIG_LOG(LOG0(Could not close LIST))
  711.          rc = FALSE;
  712.         }
  713.        } else {
  714.         CONFIG_LOG(LOG0(Could not open LIST))
  715.         rc = FALSE;
  716.        }
  717.       }
  718.      }
  719.     }
  720.  
  721.     /* Close IFF handle */
  722.     CloseIFF(iffh);
  723.    }
  724.  
  725.    /* Close file */
  726.    Close(iffh->iff_Stream);
  727.  
  728.    /* Clear execute flag */
  729.    SetProtection(name, protection | FIBF_EXECUTE);
  730.   }
  731.  
  732.   /* Free IFF handle */
  733.   FreeIFF(iffh);
  734.  }
  735.  
  736.  /* Wake application up */
  737.  SetAttrs(_app(wobj), MUIA_Application_Sleep, FALSE, TAG_DONE);
  738.  
  739.  /* No error? */
  740.  if (rc) {
  741.  
  742.   /* Create icons? */
  743.   if (icons) {
  744.    struct DiskObject *dobj;
  745.  
  746.    CONFIG_LOG(LOG0(Create icon))
  747.  
  748.    /* Get default icon for projects */
  749.    if ((dobj = GetDiskObject("ENV:Sys/def_pref")) ||
  750.        (dobj = GetDiskObjectNew(name))) {
  751.     char   *oldtool = dobj->do_DefaultTool;
  752.     char  **oldtt   = dobj->do_ToolTypes;
  753.     UBYTE   oldtype = dobj->do_Type;
  754.  
  755.     CONFIG_LOG(LOG1(DiskObject, "0x%08lx", dobj))
  756.  
  757.     /* Set new values */
  758.     dobj->do_DefaultTool = ProgramName;
  759.     dobj->do_ToolTypes   = DefaultToolTypes;
  760.     dobj->do_Type        = WBPROJECT;
  761.  
  762.     /* Write icon */
  763.     PutDiskObject(name, dobj);
  764.  
  765.     /* Reset to old values */
  766.     dobj->do_DefaultTool = oldtool;
  767.     dobj->do_ToolTypes   = oldtt;
  768.     dobj->do_Type        = oldtype;
  769.  
  770.     /* Free icon */
  771.     FreeDiskObject(dobj);
  772.    }
  773.   }
  774.  } else {
  775.  
  776.   CONFIG_LOG(LOG0(Error in config file write))
  777.  
  778.   MUI_Request(_app(wobj), wobj, 0,
  779.               TextGlobalTitle, TextGlobalCancel,
  780.               TranslateString(LOCALE_TEXT_CONFIG_WRITE_ERROR_STR,
  781.                               LOCALE_TEXT_CONFIG_WRITE_ERROR),
  782.               name);
  783.  }
  784.  
  785.  CONFIG_LOG(LOG1(Result, "%ld", rc))
  786.  
  787.  return(rc);
  788. }
  789.  
  790. /* Write configuration file using file requester */
  791. #undef  DEBUGFUNCTION
  792. #define DEBUGFUNCTION WriteConfigWithRequester
  793. const char *WriteConfigWithRequester(Object *wobj, Object **lists,
  794.                                      const char *name, BOOL icons)
  795. {
  796.  const char *newname;
  797.  
  798.  /* Open file requester */
  799.  if (newname = OpenFileRequester(wobj, name,
  800.                             TranslateString(LOCALE_TEXT_CONFIG_SAVE_FILE_STR,
  801.                                             LOCALE_TEXT_CONFIG_SAVE_FILE),
  802.                             TRUE)) {
  803.  
  804.   CONFIG_LOG(LOG2(File, "%s (0x%08lx)", newname, newname))
  805.  
  806.   /* Read configuration from file */
  807.   WriteConfig(wobj, lists, newname, icons);
  808.  }
  809.  
  810.  CONFIG_LOG(LOG1(Result, "0x%08lx", newname))
  811.  
  812.  /* Return pointer to new file name. Caller has to FreeVector() it! */
  813.  return(newname);
  814. }
  815.  
  816. /* Read a string from the IFF property and duplicate it */
  817. #undef  DEBUGFUNCTION
  818. #define DEBUGFUNCTION ReadStringProperty
  819. char *ReadStringProperty(struct IFFHandle *iffh, ULONG type, ULONG id)
  820. {
  821.  void                  *rc = NULL;
  822.  struct StoredProperty *sp;
  823.  
  824.  CONFIG_LOG(LOG3(Entry, "Handle 0x%08lx Type 0x%08lx ID 0x%08lx",
  825.                  iffh, type, id))
  826.  
  827.  /* Find property */
  828.  if (sp = FindProp(iffh, type, id)) {
  829.  
  830.   CONFIG_LOG(LOG2(Property, "Data 0x%08lx Size %ld", sp->sp_Data, sp->sp_Size))
  831.  
  832.   /* Allocate memory for property */
  833.   if (rc = GetVector(sp->sp_Size))
  834.  
  835.    /* Copy property */
  836.    CopyMem(sp->sp_Data, rc, sp->sp_Size);
  837.  }
  838.  
  839.  CONFIG_LOG(LOG1(Result, "0x%08lx", rc))
  840.  
  841.  return(rc);
  842. }
  843.  
  844. /* Write data to an IFF property */
  845. #undef  DEBUGFUNCTION
  846. #define DEBUGFUNCTION WriteProperty
  847. BOOL WriteProperty(struct IFFHandle *iffh, ULONG id, void *data, ULONG size)
  848. {
  849.  BOOL rc;
  850.  
  851.  CONFIG_LOG(LOG4(Entry, "IFFHandle 0x%08lx ID 0x%08lx Data 0x%08lx Size %ld",
  852.                  iffh, id, data, size))
  853.  
  854.  /* a) Open new property chunk */
  855.  /* b) Write data to chunk     */
  856.  /* c) Close property chunk    */
  857.  rc = (PushChunk(iffh, 0, id, IFFSIZE_UNKNOWN) == 0) &&
  858.       (WriteChunkBytes(iffh, data, size) == size) &&
  859.       (PopChunk(iffh) == 0);
  860.  
  861.  CONFIG_LOG(LOG1(Result, "%ld", rc))
  862.  
  863.  return(rc);
  864. }
  865.  
  866. /* Write string to an IFF property */
  867. #undef  DEBUGFUNCTION
  868. #define DEBUGFUNCTION WriteStringProperty
  869. BOOL WriteStringProperty(struct IFFHandle *iffh, ULONG id, const char *string)
  870. {
  871.  BOOL rc = TRUE;
  872.  
  873.  CONFIG_LOG(LOG4(Entry, "IFFHandle 0x%08lx ID 0x%08lx String %s (0x%08lx)",
  874.                  iffh, id, string, string))
  875.  
  876.  /* String valid? */
  877.  if (string)
  878.  
  879.   /* Yes, write string */
  880.   rc = WriteProperty(iffh, id, string, strlen(string) + 1);
  881.  
  882.  CONFIG_LOG(LOG1(Result, "%ld", rc))
  883.  
  884.  return(rc);
  885. }
  886.